import { HubConnectionBuilder, HubConnectionState, LogLevel } from '@microsoft/signalr';
import store from '../store/store';
import { isNumber, isString, findIndex, clone, isPlainObject } from 'lodash';
import http from '../utils/request';
import api from '../api/apis';
import { MessageBox } from 'element-ui';
import clientCode from './clientCode';
let hubClientObject = null;
export class HubClient {
  constructor(hubUrl) {
    this.hubUrl = hubUrl;
    this._statePrev = HubConnectionState.Disconnected;
    this._connection = new HubConnectionBuilder()
      .configureLogging(LogLevel.Warning)
      .withUrl(hubUrl)
      .withAutomaticReconnect({
        nextRetryDelayInMilliseconds: (retryContext) => {
          const { previousRetryCount } = retryContext;
          if (previousRetryCount === 0) {
            // 如果是第一次断线，立即重连
            return 0;
          } else if (previousRetryCount < 5) {
            // 如果尝试重连次数少于5次，使用指数退避策略增加延迟
            // 例如，第1次延迟2秒，第2次延迟4秒，依此类推
            return Math.pow(2, previousRetryCount) * 1000;
          } else {
            // 如果尝试重连次数达到5次或更多，每次尝试之间延迟30秒
            return 30000;
          }
        },
      })
      .build();
    this._connection.onclose((error) => {
      console.info("HubConnection.onclose", error);
      this._nextState();
    });
    this._connection.onreconnecting((error) => {
      console.info("HubConnection.onreconnecting", error);
      this._nextState();
    });
    this._connection.onreconnected((connectionId) => {
      console.info("HubConnection.onreconnected", connectionId);
      this._nextState();
    });
    // 监听CAD上线
    this._connection.on('online', this.online);
    // 监听 CAD 上传零件
    this._connection.on('PartUploaded', this.PartUploaded);
    // 监听 CAD 取消上传零件
    this._connection.on('PartCanceled', this.PartCanceled);
    // 监听 CAD 取消排样
    this._connection.on('LayoutCanceled', this.LayoutCanceled);
    // 监听 CAD 添加排样
    this._connection.on('LayoutUploaded', this.LayoutUploaded);
    // WEB 端注册完成
    this._connection.on('RegisterClientResult', this.RegisterClientResult);
  }

  get state() {
    return this._connection.state;
  }

  get enable() {
    return this._enable;
  }

  set enable(val) {
    this._enable = !!val;
    if (this._enable) {
      setTimeout(() => {
        this._tryStart();
      }, 0);
    } else {
      setTimeout(() => {
        this._tryStop();
      }, 0);
    }
  }

  /**
   * @overload
   * @param {"OnDeviceConnected"} event
   * @param {(e: {deviceId: string; ip: string; status: string[];}) => any} handler
   * @returns {() => void}
   */
  on(event, handler) {
    const { _connection } = this;
    _connection.on(event, handler);
    return () => _connection.off(event, handler);
  }

  /** @private */
  _nextState() {
    const prev = this._statePrev;
    const next = this._connection.state
    if (prev !== next) {
      this._statePrev = next;
    }
  }

  /** @private */
  _tryStart() {
    if (!this.enable) return;
    if (this._connection.state !== HubConnectionState.Disconnected) return;
    this._connection.start()
      .catch(err => console.error(err))
      .finally(() => {
        this._nextState();
      });
  }

  /** @private */
  _tryStop() {
    if (this.enable) return;
    if (this._connection.state !== HubConnectionState.Connected) return;
    this._connection.stop()
      .catch(err => console.error(err))
      .finally(() => {
        this._nextState();
      });
  }


  /* 监听数据 监听数据 */

  /**
   * WEB 登录注册监听
   * @returns {Promise<ApiResult<RegisterClientResult<Result,message>>}
   */
  RegisterClientResult(r, msg) {
    console.log('web端注册完成', r, msg)
  }


  /**
   * CAD已上线
   * @returns {Promise<ApiResult<online<null>>>}
   */
  online(e) {
    console.log('cad已上线', e);
    const uploadPartInfo = store.state.resource.uploadPartInfo;
    const uploadLayoutInfo = store.state.resource.uploadLayoutInfo;
    console.log('零件数据', uploadPartInfo);
    console.log('排样数据', uploadLayoutInfo);
    if (uploadPartInfo && isString(uploadPartInfo)) {
      // 如果有零件上传数据cad上线后调用上传
      if (hubClientObject) {
        hubClientObject.sendCreatePart(uploadPartInfo)
      } else {
        store.dispatch('resource/cadBroadcastLineUpload', { type: 'part', data: uploadPartInfo })
      }
      store.dispatch('resource/setUploadPartInfo', null);
    }

    if (uploadLayoutInfo && isPlainObject(uploadLayoutInfo)) {
      // 如果有排样上传数据cad上线后调用上传
      if (hubClientObject) {
        hubClientObject.sendLayout(uploadLayoutInfo)
      } else {
        store.dispatch('resource/cadBroadcastLineUpload', { type: 'layout', data: uploadLayoutInfo })
      }
    }
  };

  /**
   * CAD上传零件解析完成
   * @returns {Promise<ApiResult<PartUploaded[]>>}
   */
  PartUploaded(e) {
    console.log('CAD上传零件解析完成', e)
    let formData = null;
    if (isPlainObject(e)) {
      formData = e;
    } else if (isString(e)) {
      try {
        formData = JSON.parse(e)
      } catch (err) { console.log(err) }
    }
    if (formData && formData.data && Array.isArray(formData.data.urls) && formData.data.urls.length > 0) {
      store.dispatch('app/setCADpartPath', formData.data.urls);
      store.dispatch('resource/setUploadPartInfo', null);
    }
  };

  /**
   * CAD取消零件上传
   * @returns {Promise<ApiResult<PartCanceled[]>>}
   */
  PartCanceled(e) {
    if (e) {
      MessageBox({ title: '提示', message: "提交CAD 零件上传任务已取消", showCancelButton: false })
      store.dispatch('resource/setUploadPartInfo', null);
    }
  }


  /**
   *  CAD 添加排样结果完成
   * @returns {Promise<ApiResult<LayoutUploaded[]>>}
   */
  LayoutUploaded(e) {
    console.log('CAD排样结果完成', e)
    let formData = null;
    if (isPlainObject(e)) {
      formData = e;
    } else if (isString(e)) {
      try {
        formData = JSON.parse(e)
      } catch (err) { console.log(err) }
    }
    store.dispatch('resource/setUploadLayoutInfo', null);
    if (formData && formData.data && Array.isArray(formData.data.urls) && formData.data.urls.length > 0) {
      store.dispatch('app/setCADLayout', formData.data.urls)
      http.post(api.PRODUCTION, formData.data.urls)
        .then(res => {
          MessageBox({ title: '提示', message: "提交CAD排样结果已添加成功!", showCancelButton: false })
        })
    }
  }

  /**
   * CAD取消排样
   * @returns {Promise<ApiResult<LayoutCanceled[]>>}
   */
  LayoutCanceled(e) {
    console.log('CAD取消排样', e)
    let formData = null;
    if (isPlainObject(e)) {
      formData = e;
    } else if (isString(e)) {
      try {
        formData = JSON.parse(e)
      } catch (err) { console.log(err) }
    }
    if (formData) {
      let dto = [];
      let taskParts = Array.isArray(formData.Parts) ? formData.Parts : [];
      taskParts.forEach(item => {
        dto.push({
          taskId: item.taskId,
          partId: item.partId
        })
      })
      store.dispatch('resource/setUploadLayoutInfo', null);
      http.post(api.SET_LAYOUT_UNLOCK, dto)
        .then(res => {
          MessageBox({ title: '提示', message: "提交CAD排样任务已取消,系统已自动解锁零件锁定数量", showCancelButton: false })
          store.dispatch('app/setPartRefreshList');
        })
    }
  }






  /* 方法调用 方法调用 */
  /**
   * d调起cad并传入参数
   * @returns {Promise<ApiResult<CreatePart[]>>}
   */
  sendCreatePart(taskId) {
    return this._connection.invoke('SendMessage', 'CreatePart', taskId);
  }

  /**
   * 排样传值CAD
   * @param {array[]} dto
   * @returns {Promise<ApiResult<Layout[]>>}
   */
  sendLayout(dto) {
    console.log('dto', JSON.stringify(dto));
    return this._connection.invoke('SendMessage', 'Layout', JSON.stringify(dto));
  }

  /**
   * 进入系统后登陆下
   * @param {null}
   * @returns {Promise<ApiResult<RegisterClient[]>>}
   */
  sendRegisterClient() {
    return this._connection.invoke('SendMessage', 'RegisterClient', clientCode.web);
  }

  /**
   * 启动CAD 程序
   * @param {null}
   * @returns {Promise<ApiResult<RegisterClient[]>>}
   */
  sendOpenClient() {
    let processType=store.state.app.processType;
    return this._connection.invoke('SendMessageToClient', 'OpenClient', (processType == 1?clientCode.cad_bc:clientCode.cad_xc), clientCode.app)
  }

  // 判断客户端 是否在线
  sendlsClientOnline(code = 'cad') {
    let hubCode = '';
    if (code == 'cad') {
      let processType=store.state.app.processType;
      hubCode = (processType == 1?clientCode.cad_bc:clientCode.cad_xc);
    } else if (code == 'app') {
      hubCode = clientCode.app;
    } else if (code == 'web') {
      hubCode = clientCode.web;
    }
    return this._connection.invoke('IsClientOnline', hubCode)
  }
}
const hubClient = (apiServerUrl) => {
  hubClientObject = new HubClient(apiServerUrl + "/HostHub");
  return hubClientObject;
};
export default hubClient;
